package com.xncoding.netty.socket.my.nio.demo00;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NioClient {

    public static void main(String[] args) throws IOException {
        String host = "127.0.0.1";
        int port = 6667;
        // 1、获取Selector选择器，Selector内部采用Reactor模式
        Selector selector = Selector.open();
        // 2、获取服务端通道SocketChannel
        SocketChannel socketChannel = SocketChannel.open();
        // 3.设置为非阻塞
        socketChannel.configureBlocking(false);
        // 4、绑定ip连接端口
        boolean isConnect = socketChannel.connect(new InetSocketAddress(host, port));
        // 5、socketChannel注册selector,并注册的操作为：“可读”或者“连接就绪"操作
        if (isConnect) {
            //读就绪事件，表示通道中已经有了可读的数据，可以执行读操作了
            socketChannel.register(selector, SelectionKey.OP_READ);
        } else {
            //连接就绪事件，表示客户与服务器的连接已经建立成功
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
        }
        // 6、采用轮询的方式，查询获取“准备就绪”的注册过的操作
        while (true) {
            try {
                //6.1、1s唤醒一次，没有收到前是阻塞的
                selector.select(1000);
                // 7、获取当前选择器中所有注册的选择键（“已经准备就绪的操作”）
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                // 7.1、使用迭代器进行遍历
                Iterator<SelectionKey> it = selectedKeys.iterator();
                SelectionKey key = null;
                while (it.hasNext()) {
                    // 8、获取“准备就绪”的key
                    key = it.next();
                    it.remove();
                    try {
                        // 8.1、判断当前key是否有效
                        if (!key.isValid()){
                            continue;
                        }
                        // 9、判断key是具体的什么事件
                        if (key.isConnectable()) {
                            // 10、判断连接是否就绪，就绪就发送消息
                            SocketChannel socketChannelKey = (SocketChannel) key.channel();
                            if (socketChannelKey.finishConnect()) { //判断是否建立连接，如果没有则阻塞
                                //如果连接成功，将socket注册到selector上，监听OP_READ操作
                                socketChannelKey.register(selector, SelectionKey.OP_READ);
                                //发送消息到服务端
                                byte[] req = "这是客户端发送的消息".getBytes();
                                ByteBuffer byteBuffer = ByteBuffer.allocate(req.length);
                                byteBuffer.put(req);
                                byteBuffer.flip();
                                socketChannelKey.write(byteBuffer);
                                if (!byteBuffer.hasRemaining()) {
                                    System.out.println("客户端发送消息成功");
                                }
                            } else {
                                System.exit(1);
                            }
                        }
                        //11、判断是否有消息可读的时候，服务端返回的消息
                        if (key.isReadable()) {
                            //分配1M的缓冲区
                            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                            //读取服务端消息
                            int readBytes = socketChannel.read(readBuffer);
                            if (readBytes > 0) {
                                readBuffer.flip();
                                byte[] bytes = new byte[readBuffer.remaining()];
                                readBuffer.get(bytes);
                                String body = new String(bytes, "utf-8");
                                System.out.println("服务端回复： " + body);
                            } else if (readBytes < 0) {
                                key.cancel();
                                socketChannel.close();
                            } else {
                                ;//读到0字节，忽略
                            }
                        }

                } catch (IOException e) {
                    if (key != null) {
                        key.cancel();
                        if (key.channel() != null) {
                            key.channel().close();
                        }
                    }
                }
                }
            } catch (Exception ignore) {
                ignore.printStackTrace();
                System.exit(1);
            }
        }

    }
}
